package com.reebake.ideal.security.oauth2.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.reebake.ideal.json.util.JsonUtil;
import com.reebake.ideal.security.entity.AccessTokenEntity;
import com.reebake.ideal.security.entity.UserDetailsEntity;
import com.reebake.ideal.security.entity.UserThirdPartyEntity;
import com.reebake.ideal.security.oauth2.client.OAuth2BindUserRepository;
import com.reebake.ideal.security.oauth2.entity.ClientRegistrationEntity;
import com.reebake.ideal.security.oauth2.entity.OAuth2BindUser;
import com.reebake.ideal.security.oauth2.exception.OAuth2BindUserNotFoundException;
import com.reebake.ideal.security.oauth2.properties.OAuth2ClientSecurityProperties;
import com.reebake.ideal.security.oauth2.service.ClientRegistrationService;
import com.reebake.ideal.security.oauth2.vo.ClientRegistrationSimpleVO;
import com.reebake.ideal.security.oauth2.vo.UserBindResultVO;
import com.reebake.ideal.security.oauth2.vo.UserBindVO;
import com.reebake.ideal.security.service.UserSecurityService;
import com.reebake.ideal.security.util.JwtAuthUtil;
import com.reebake.ideal.verify.code.core.VerifyCodeService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@RequestMapping("/oauth2/client-registration")
public class ClientRegistrationAuthController {
    private final ClientRegistrationService clientRegistrationService;
    private final OAuth2BindUserRepository oAuth2BindUserRepository;
    private final UserSecurityService userSecurityService;
    private final VerifyCodeService verifyCodeService;
    private final PasswordEncoder passwordEncoder;
    private final OAuth2ClientSecurityProperties oAuth2ClientSecurityProperties;

    @GetMapping("/all")
    public List<ClientRegistrationSimpleVO> queryAll() {
        List<ClientRegistrationEntity> clients = clientRegistrationService.list();
        return clients.stream().map(entity -> {
            ClientRegistrationSimpleVO vo = new ClientRegistrationSimpleVO();
            vo.setRegistrationId(entity.getRegistrationId());
            Map<String, Object> configurationMetadata = JsonUtil.toBean(entity.getConfigurationMetadata(), new TypeReference<Map<String, Object>>() {
            });
            String iconName = (String) configurationMetadata.get(LambdaUtil.getFieldName(ClientRegistrationSimpleVO::getIconName));
            vo.setIconName(iconName);
            String iconColor = (String) configurationMetadata.get(LambdaUtil.getFieldName(ClientRegistrationSimpleVO::getIconColor));
            vo.setIconColor(iconColor);
            return vo;
        }).collect(Collectors.toList());
    }

    @PostMapping("bind")
    public UserBindResultVO bindUser(@Valid @RequestBody UserBindVO userBindVO) {
        String securityId = Base64.decodeStr(userBindVO.getSecurityId());
        OAuth2BindUser oAuth2BindUser = oAuth2BindUserRepository.get(securityId);
        if (ObjUtil.isNull(oAuth2BindUser)) {
            throw new OAuth2BindUserNotFoundException();
        }
        String username = userBindVO.getUsername();
        if (StrUtil.isBlank(username)) {
            username = userBindVO.getReceiver();
        }
        UserDetailsEntity userDetailsEntity = null;
        if (StrUtil.isNotBlank(userBindVO.getUsername()) && StrUtil.isNotBlank(userBindVO.getPassword())) {
            userDetailsEntity = userSecurityService.loadUserByUsername(userBindVO.getUsername());
            if (ObjUtil.isNull(userDetailsEntity)) {
                throw new UsernameNotFoundException("username not found.");
            }
            boolean checkResult = passwordEncoder.matches(userBindVO.getPassword(), userDetailsEntity.getPassword());
            if(!checkResult) {
                throw new BadCredentialsException("password not match");
            }
        } else if (StrUtil.isNotBlank(userBindVO.getReceiver()) && StrUtil.isNotBlank(userBindVO.getVerifyCode())) {
            Boolean result = verifyCodeService.check(userBindVO.getReceiver(), userBindVO.getVerifyCode());
            if (BooleanUtil.isTrue(result)) {
                userDetailsEntity = userSecurityService.loadUserByPhoneNumber(userBindVO.getReceiver());
                if(ObjUtil.isNull(userDetailsEntity)) {
                    throw new UsernameNotFoundException("username not found.");
                }
            }else {
                throw new BadCredentialsException("verify failure");
            }
        }

        String userId = userDetailsEntity.getUserId();
        Collection<String> authorities = userDetailsEntity.getAuthorities();
        AccessTokenEntity accessTokenEntity = JwtAuthUtil.generate(username, userId, authorities, oAuth2ClientSecurityProperties.getAccessTokenExpireIn());

        UserThirdPartyEntity userThirdPartyEntity = JsonUtil.toBean(JsonUtil.toJsonStr(oAuth2BindUser.getAttributes()), UserThirdPartyEntity.class);
        userThirdPartyEntity.setUserId(userId);
        userSecurityService.bindThirdParty(userThirdPartyEntity);

        return createToken(securityId, accessTokenEntity.getAccessToken());
    }

    private UserBindResultVO createToken(String securityId, String accessToken) {
        oAuth2BindUserRepository.remove(securityId);
        UserBindResultVO userBindResultVO = new UserBindResultVO();
        userBindResultVO.setAccessToken(accessToken);
        return userBindResultVO;
    }
}
